﻿namespace Resharper.Abstractions.CodeDom.Accessors
{
  using System.Collections.Generic;
  using System.Linq;
  using JetBrains.Annotations;
  using JetBrains.ReSharper.Psi;
  using JetBrains.ReSharper.Psi.CSharp.Tree;
  using JetBrains.ReSharper.Psi.Tree;
  using Resharper.Abstractions.CodeDom.Statements.Blocks;
  using Resharper.Abstractions.CodeDom.TreeNodes;
  using Resharper.Abstractions.CodeDom.TypeMembers.Methods;

  /// <summary>Defines the <see cref="Accessor"/> class.</summary>
  public class Accessor : TreeNode, TypeMembers.Functions.IFunction
  {
    #region Constructors and Destructors

    /// <summary>Initializes a new instance of the <see cref="Accessor"/> class.</summary>
    /// <param name="accessor">The function.</param>
    internal Accessor([NotNull] IAccessorDeclaration accessor) : base(accessor)
    {
    }

    #endregion

    #region Implemented Interfaces

    #region IFunction

    /// <summary>
    /// Gets the body.
    /// </summary>
    public Block Body
    {
      get
      {
        var body = this.InternalAccessor.Body;

        return body != null ? new Block(body) : null;
      }

      set
      {
        if (value == null)
        {
          this.InternalAccessor.SetBody(null);
          return;
        }

        this.InternalAccessor.SetBody(value.InternalBlock);
      }
    }

    /// <summary>
    /// Gets the type of the return.
    /// </summary>
    public Type ReturnType
    {
      get
      {
        var declaredElement = this.InternalAccessor.DeclaredElement;
        if (declaredElement == null)
        {
          return null;
        }

        return new Type(declaredElement.ReturnType, this.InternalAccessor.Language);
      }
    }

    #endregion

    #region IParametersOwner

    /// <summary>
    /// The parameters field.
    /// </summary>
    [NotNull]
    public IEnumerable<Parameter> Parameters
    {
      get
      {
        var accessor = this.InternalAccessor.DeclaredElement;
        if (accessor == null)
        {
          yield break;
        }

        foreach (var parameter in accessor.Parameters)
        {
          var declaration = parameter.GetDeclarations().FirstOrDefault() as IParameterDeclaration;
          if (declaration == null)
          {
            continue;
          }

          yield return new Parameter(declaration);
        }
      }
    }

    #endregion

    #endregion

    #region Properties

    /// <summary>
    /// Gets the access right.
    /// </summary>
    public CodeDom.AccessRights AccessRights
    {
      get
      {
        return AccessRightsHelper.GetAccessRights(this.InternalAccessor.DeclaredElement);
      }
    }

    /// <summary>
    /// Gets a value indicating whether this instance is getter.
    /// </summary>
    public bool IsGetter
    {
      get
      {
        return this.InternalAccessor.Kind == AccessorKind.GETTER;
      }
    }

    /// <summary>
    /// Gets a value indicating whether this instance is setter.
    /// </summary>
    public bool IsSetter
    {
      get
      {
        return this.InternalAccessor.Kind == AccessorKind.SETTER;
      }
    }

    /// <summary>
    /// Gets or sets the internal function.
    /// </summary>
    /// <value>The internal function.</value>
    [NotNull]
    internal IAccessorDeclaration InternalAccessor
    {
      get
      {
        return (IAccessorDeclaration)this.InternalTreeNode;
      }
    }

    #endregion

    #region Public Methods

    /// <summary>Gets the accessor.</summary>
    /// <param name="treeNode">The tree node.</param>
    public static Accessor GetAccessor(object treeNode)
    {
      var accessor = treeNode as IAccessorDeclaration;

      return accessor != null ? new Accessor(accessor) : null;
    }

    #endregion
  }
}